home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Visual Database / Visual dBase v5.5 / EXTERN.PAK / DBEXTERN.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-18  |  20.0 KB  |  686 lines

  1. //*****************************************************************************
  2. //
  3. // FILE:        DBExtern.cpp
  4. //
  5. // WRITTEN BY:  Keimpe
  6. //
  7. // DATE:        1/94
  8. //
  9. // UPDATED:     5/94
  10. //
  11. // REVISION:    $Revision:   2.10  $
  12. //
  13. // VERSION:     Visual dBASE
  14. //
  15. // DESCRIPTION:
  16. //              C++ part of the example that shows how the EXTERN system
  17. //              can be used.
  18. //
  19. //              For a description of this example, see the dbextern.prg file.
  20. //
  21. //*****************************************************************************
  22.  
  23.    // Include some headers.
  24. #include "dbasevar.h"
  25.  
  26. //============================================================================
  27. //
  28. //  You can create an instance of class CSession in DBaseInitInstance()
  29. //  and set it up. In subsequent call backs, a pointer to this instance
  30. //  can be retrieved with a call to GetSession().  Data that is kept on
  31. //  a per-instance basis (DBaseVars for example) should be kept in the
  32. //  session object.
  33. //
  34. //============================================================================
  35.  
  36. class CSession {
  37. public:
  38.    DVar ThisSessionLocalObject;
  39.  
  40.    CSession(){
  41.        // Initialize objects local to this session.
  42.  
  43.        time_t t = time( NULL );
  44.        ThisSessionLocalObject->SetZString( ctime( &t ) );
  45.    }
  46.    ~CSession(){
  47.        // Destroy objects local to this session.
  48.    }
  49.  
  50.    char * UseLocalObject(){
  51.        // Use objects local to this session.
  52.        return ThisSessionLocalObject->String();
  53.    }
  54. };
  55.  
  56.  
  57. extern "C" {
  58. //============================================================================
  59. //
  60. //  Usual DLL functions.
  61. //
  62. //============================================================================
  63.  
  64. int FAR PASCAL LibMain(HINSTANCE, WORD, WORD, LPSTR){
  65.  
  66.    return 1;
  67. }
  68. int CALLBACK WEP(int /*nParam*/){return(1);}
  69.  
  70. //
  71. //  This function is called every time an Instance of Visual dBASE loads the DLL.
  72. //  If for some reason the DLL determines that it cannot load, it can return
  73. //  an error.  If the DLL loads properly it should return DBASE_INIT_OK.
  74. //
  75.  
  76. int CALLBACK DBaseInitInstance(void){
  77.  
  78.    asm int 3;     // break point for the debugger.
  79.  
  80.    DBase()->SetSession(new CSession());
  81.  
  82.        //
  83.        // Possible Error return Values
  84.        //
  85.        //return DBASE_INIT_NO_MULTI_INSTANCE;
  86.        //return DBASE_INIT_ERROR;
  87.  
  88.    return DBASE_INIT_OK;
  89. }
  90.  
  91. //
  92. //  DBaseExitInstance() is called when an instance of dBASE terminates or
  93. //  manually unloads a DLL through the RELEASE DLL command.  This give the
  94. //  DLL a chance to free any resources associated with a running instance.
  95. //
  96.  
  97. void CALLBACK DBaseExitInstance(void){
  98. }
  99.  
  100. //============================================================================
  101. //
  102. // Example # 1.
  103. //
  104. // Some simple examples using the different types.
  105. //
  106. //============================================================================
  107.  
  108. BOOL _export _pascal TypeTest1( char * Str, int Int, DoubleType Double ) {
  109.    if( strcmp( Str, "string" ) != 0 || Int != 123 || Double != 321.123L )
  110.        return 0;
  111.    return 1;
  112. }
  113.  
  114. DoubleType _export _cdecl TypeTest2( char * Str, long Long, DBaseVar *Var ) {
  115.    return strtod( Str, NULL ) + (DoubleType) Long + Var->Double();
  116. }
  117.  
  118. //============================================================================
  119. //
  120. // Example # 3.
  121. //
  122. // Visual dBASE objects can be of various types. With the Type() function you
  123. // can recognize what kind of object you're dealing with.
  124. //
  125. //============================================================================
  126.  
  127. char* _export _pascal WhichType( DBaseVar *var ) {
  128.  
  129.    static char buffer[25];
  130.  
  131.        // Get the type and put it in buffer.
  132.    switch( var->Type() ){
  133.    case 'L':                          // Logical
  134.        strcpy( buffer, "Logical " );
  135.        strcat( buffer, var->Logical() ? ".T." : ".F.");
  136.        break;
  137.    case 'N':                          // Numeric double
  138.        strcpy( buffer, "Double " );
  139.        gcvt(var->Double(),10,buffer+7);
  140.        break;
  141.    case 'I':                          // Numeric long
  142.        strcpy( buffer, "Long " );
  143.        ltoa(var->Long(), buffer+5, 10);
  144.        break;
  145.    case 'C':                          // String
  146.        strcpy( buffer, "String " );
  147.        strcat( buffer, var->String() );
  148.        break;
  149.    case 'O':                          // Object
  150.        strcpy( buffer, "Object" );
  151.        break;
  152.    case 'F':                          // Function or code block
  153.        strcpy( buffer, "Code" );
  154.        break;
  155.    default:                           // Anybody else
  156.       buffer[0] = var->Type();
  157.       buffer[1] = ' ';
  158.       strcpy( buffer+2, ":Unknown Type" );
  159.    }
  160.        // Return the type.
  161.    return buffer;
  162. }
  163.  
  164. //============================================================================
  165. //
  166. // Example # 4.
  167. //
  168. // You can modify Visual dBASE objects passed in.  Since they are passed by
  169. // reference, changes made here to objects show also on the Visual dBASE side.
  170. // It is also possible to use the CVAR type as the return type of Visual dBASE
  171. // functions.  On the Visual dBASE side you simply declare the function
  172. // with CVAR as the return type:
  173. //
  174. //   EXTERN CDECL CVAR ModifyAndAdd1( CVAR, CVAR ) dbextern.dll
  175. //   EXTERN CVAR ModifyAndAdd2( CVAR, CVAR ) dbextern.dll
  176. //
  177. // but on the C/C++ side you use void as the return type of the function
  178. // and list the CVAR return variable as the first parameter for CDECL
  179. // functions or as the last parameter for PASCAL functions.
  180. //
  181. //============================================================================
  182.  
  183. void _export _cdecl ModifyAndAdd1( DBaseVar *ReturnValue,
  184.                                    DBaseVar *D1, DBaseVar *D2 ) {
  185.  
  186.    ReturnValue->SetDouble( D1->Double() + (DoubleType)D2->Long() );
  187.    D1->SetZString( "Changed to a string" );
  188.    D2->SetLogical( 1 );
  189. }
  190.  
  191. void _export _pascal ModifyAndAdd2( DBaseVar *D1, DBaseVar *D2,
  192.                                     DBaseVar *ReturnValue ) {
  193.  
  194.    ReturnValue->SetDouble( D1->Double() + (DoubleType)D2->Long() );
  195.    D1->SetZString( "Changed in ModifyAndAdd2" );
  196.    D2->SetLogical( 1 );
  197. }
  198.  
  199. //============================================================================
  200. //
  201. // Example # 5.
  202. //
  203. // Parts of Visual dBASE class objects are accessible on this side.
  204. // This example shows one way of how to implement Windows API calls
  205. // that require the use of structs.
  206. //
  207. //============================================================================
  208.  
  209. void _export _cdecl WSetRect( DBaseVar *rect, WORD left, WORD top,
  210.                                WORD right, WORD bottom ){
  211.    RECT wrect;
  212.    DVar Temp;
  213.  
  214.        // Call the Windows API.
  215.    SetRect( &wrect, left, top, right, bottom );
  216.  
  217.        // Fill in the DBaseVar.
  218.    rect->SetProperty( "left", DVar( (long)(wrect.left) ) );
  219.    rect->SetProperty( "top", DVar( (long)(wrect.top) ) );
  220.    rect->SetProperty( "right", DVar( (long)(wrect.right) ) );
  221.    rect->SetProperty( "bottom", DVar( (long)(wrect.bottom) ) );
  222. }
  223.  
  224. void _export _pascal WIntersectRect( DBaseVar *result,
  225.                                      DBaseVar *rect1, DBaseVar *rect2 ){
  226.    RECT one, two, three;
  227.    DVar Temp;
  228.  
  229.        // Copy rect1 to one.
  230.    rect1->Property( "left", Temp );
  231.    one.left = Temp->Long();
  232.    rect1->Property( "top", Temp );
  233.    one.top = Temp->Long();
  234.    rect1->Property( "right", Temp );
  235.    one.right = Temp->Long();
  236.    rect1->Property( "bottom", Temp );
  237.    one.bottom = Temp->Long();
  238.  
  239.        // Copy rect2 to two.
  240.    rect2->Property( "left", Temp );
  241.    two.left = Temp->Long();
  242.    rect2->Property( "top", Temp );
  243.    two.top = Temp->Long();
  244.    rect2->Property( "right", Temp );
  245.    two.right = Temp->Long();
  246.    rect2->Property( "bottom", Temp );
  247.    two.bottom = Temp->Long();
  248.  
  249.        // Make the Windows API call
  250.    IntersectRect( &three, &one, &two );
  251.  
  252.        // Copy three to result.
  253.    Temp->SetLong( three.left );
  254.    result->SetProperty( "left", Temp );
  255.    Temp->SetLong( three.top );
  256.    result->SetProperty( "top", Temp );
  257.    Temp->SetLong( three.right );
  258.    result->SetProperty( "right", Temp );
  259.    Temp->SetLong( three.bottom );
  260.    result->SetProperty( "bottom", Temp );
  261. }
  262.  
  263. //============================================================================
  264. //
  265. // Example # 6.
  266. //
  267. // Through the use of code blocks Visual dBASE code can be executed on the fly
  268. // inside the DLL. The 2 objects that are passed in are compared, changed,
  269. // and compared again, all through codeblocks.
  270. //
  271. //============================================================================
  272.  
  273. BOOL _cdecl AreEqual( DBaseVar *one, DBaseVar * two ){
  274.  
  275.    DVar Temp, Result;
  276.  
  277.        // Check that the types are equal.
  278.    if( one->Type() != two->Type() )
  279.        return 0;
  280.  
  281.        // Compare them and return the result.
  282.    Temp->SetCodeBlock( "{|a,b| a=b}" );
  283.    Temp->RunCodeBlock( Result,2,&one );
  284.    return Result->Logical();
  285. }
  286.  
  287. char * _export _pascal CheckAndChange( DBaseVar *one, DBaseVar *two ) {
  288.  
  289.    BOOL Result1, Result2;
  290.    DVar Temp1, Temp2, Temp3( 3.0L );
  291.  
  292.        // Temp3Ptr is used to pass a DVar to a function that
  293.        // expects a DBaseVar**.
  294.    DBaseVar * Temp3Ptr = Temp3;
  295.  
  296.        // Announce that we arrived.
  297.    Temp1->SetCodeBlock( "{ ;? 'Inside CheckAndChange' }" );
  298.    Temp1->RunCodeBlock( Temp2, 0, (DVar*)0 );
  299.  
  300.        // Check if they are equal coming in.
  301.    Result1 = AreEqual( one, two );
  302.  
  303.        // Change the 2 objects through codeblocks.
  304.    Temp1->SetCodeBlock( "{|| 4.0 }" );
  305.    Temp1->RunCodeBlock( one, 0, (DVar*)0 );
  306.    Temp1->SetCodeBlock( "{ |a| a }" );
  307.    Temp1->RunCodeBlock( two, 1, &Temp3Ptr );
  308.  
  309.        // Check that they are not equal anymore.
  310.    Result2 = AreEqual( one, two );
  311.  
  312.        // Return the result.
  313.    if( Result1 == 1 && Result2 == 0 )
  314.        return "passed"; else return "failed";
  315. }
  316.  
  317. //============================================================================
  318. //
  319. // Example # 7.
  320. //
  321. // Visual dBASE arrays can be accessed in the DLL.  This example computes the
  322. // average of the members of an array created on the Visual dBASE side.
  323. // The DLL function is called without any parameters.  To be able to
  324. // get at the array, the DLL function is executed through a function
  325. // pointer that is a member of the same object as the array.  Inside
  326. // the DLL you can get the "this" pointer of the object the caller
  327. // belongs to, and through that "this" pointer you can access the
  328. // array members.
  329. // Normally ofcourse you can just pass the array into a DBaseVar.
  330. //
  331. //============================================================================
  332.  
  333. DoubleType _export _cdecl ComputeAverage(){
  334.  
  335.    DoubleType d = 0;
  336.    long iSize;
  337.    int iCount=0;
  338.    int i = 1;
  339.    DVar pValue;
  340.    DVar Size;
  341.  
  342.        // pSize is used to pass Size to functions expecting a DBaseVar**.
  343.    DBaseVar *pSize = Size;
  344.  
  345.        // Initialize pThis with the "this" of the object the caller
  346.        // belongs to.
  347.    DVar pThis(DBase()->GetThis());
  348.  
  349.        // If we're not inside the context of a memberfunction return.
  350.    if(pThis == 0) return 0;
  351.  
  352.        // Get the Size of the array of the object.
  353.    pThis->Property("SIZE",Size);
  354.    iSize = Size->Long();
  355.  
  356.        // Loop thru the array and total the elements.
  357.    while( i < iSize){
  358.       Size->SetLong(i);
  359.       pThis->Element(pValue,1,&pSize);
  360.       DoubleType d1;
  361.       d += pValue->Double();
  362.       iCount++;
  363.       i++;
  364.    }
  365.        // Return the average.
  366.    return d/iCount;
  367. }
  368.  
  369. //============================================================================
  370. //
  371. // Example # 8.
  372. //
  373. // This example puts into "practice" a majority of the available
  374. // memberfunctions of the DBaseVar class.
  375. //
  376. //============================================================================
  377.  
  378. long _pascal _export TestABunch( DBaseVar *Var ){
  379.  
  380.    DVar Elems1[6], Elems2[6];
  381.    DVar Temp;
  382.    long Errors = 0;
  383.  
  384.        // Set up 6 different objects. Check returns of Set.. functions.
  385.    if( Elems1[0]->SetLogical( 1 ) != 1 )
  386.        Errors += 1;
  387.    if( Elems1[1]->SetDouble( 2.0L ) != 2.0L )
  388.        Errors += 2;
  389.    if( Elems1[2]->SetLong( 3L ) != 3L )
  390.        Errors += 4;
  391.    if( strcmp( Elems1[3]->SetZString( "Hi" ), "Hi" ) != 0 )
  392.        Errors += 8;
  393.    if( Elems1[4]->SetLong( 100L ) != 100L )
  394.        Errors += 16;
  395.    if( Elems1[5]->SetDouble( 5.5L ) != 5.5L )
  396.        Errors += 32;
  397.  
  398.        // pParams is used to be able to pass params in one block.
  399.    DBaseVar *pParams[2];
  400.    DVar      Param1;
  401.    DVar      Param2;
  402.  
  403.        // Set up the array of params.
  404.    pParams[0] = Param1;
  405.    pParams[1] = Param2;
  406.  
  407.        // Make a 2 by 3 array.
  408.    Temp->SetCodeBlock("{|a,b| new array(a,b)}");
  409.    Param1->SetLong(2);
  410.    Param2->SetLong(3);
  411.    Temp->RunCodeBlock(Var,2,pParams);
  412.  
  413.        // Fill in the array.
  414.    for( int i=0; i<6; i++ ){
  415.        Param1->SetLong( 1 + i % 2 );    // 1 - 2
  416.        Param2->SetLong( 1 + i % 3 );    // 1 - 3
  417.        Var->SetElement( 2,pParams,Elems1[i] );
  418.    }
  419.  
  420.        // Read back the array into a different variable.
  421.    for( i=0; i<6; i++ ){
  422.        Param1->SetLong( 1 + i % 2 );    // 1 - 2
  423.        Param2->SetLong( 1 + i % 3 );    // 1 - 3
  424.        Var->Element( Elems2[i],2,pParams );
  425.    }
  426.  
  427.        // Now check if it worked.
  428.    Temp->Set( Elems2[0] );
  429.    if( Temp->Logical() != 1 || Temp->Type() != 'L' )
  430.        Errors += 64;
  431.    if( !AreEqual( Elems1[0], Elems2[0] ) )
  432.        Errors += 64;
  433.    Temp->Set( Elems2[1] );
  434.    if( Temp->Double() != 2.0L || Temp->Type() != 'N' )
  435.        Errors += 128;
  436.    if( !AreEqual( Elems1[1], Elems2[1] ) )
  437.        Errors += 128;
  438.    Temp->Set( Elems2[2] );
  439.    if( Temp->Long() != 3L || Temp->Type() != 'I' )
  440.        Errors += 256;
  441.    if( !AreEqual( Elems1[2], Elems2[2] ) )
  442.        Errors += 256;
  443.    Temp->Set( Elems2[3] );
  444.    if( strcmp( Temp->String(), "Hi" ) != 0 || Temp->Type() != 'C' )
  445.        Errors += 512;
  446.    if( !AreEqual( Elems1[3], Elems2[3] ) )
  447.        Errors += 512;
  448.    Temp->Set( Elems2[4] );
  449.    if( Temp->Long() != 100L || Temp->Type() != 'I' )
  450.        Errors += 1024;
  451.    if( !AreEqual( Elems1[4], Elems2[4] ) )
  452.        Errors += 1024;
  453.    Temp->Set( Elems2[5] );
  454.    if( Temp->Double() != 5.5L || Temp->Type() != 'N' )
  455.        Errors += 2048;
  456.    if( !AreEqual( Elems1[5], Elems2[5] ) )
  457.        Errors += 2048;
  458.  
  459.        // And check Var using properties.
  460.    Var->Property( "SIZE", Temp );
  461.    if( Temp->Long() != 6 )
  462.        Errors += 4096;
  463.    Var->Property( "DIMENSIONS", Temp );
  464.    if( Temp->Long() != 2 )
  465.        Errors += 4096;
  466.  
  467.    return Errors;
  468. }
  469.  
  470. //============================================================================
  471. //
  472. // Example # 9.
  473. //
  474. // This example grabs the DOS environment, puts it in a string and sends
  475. // it over to the Visual dBASE side.
  476. //
  477. //============================================================================
  478.  
  479. void _export _cdecl GetEnvString(DBaseVar *pVarReturn){
  480.  
  481.    char *pEnviron = *environ;
  482.  
  483.        // Find the length and set it.
  484.    while(strlen(pEnviron) != 0){
  485.       pEnviron += strlen(pEnviron)+1;
  486.    }
  487.    int environLen = pEnviron - *environ;
  488.  
  489.        // Make room for the string and copy it.
  490.    pVarReturn->SetStringLen(environLen);
  491.    memcpy(pVarReturn->StringBuffer(),*environ,environLen);
  492.  
  493.        // Go through the string and change the zero characters into blanks.
  494.    int count = 0;
  495.    pEnviron = pVarReturn->StringBuffer();
  496.  
  497.    while( count < (environLen - 1) ) {
  498.        if( *( pEnviron + count ) == 0x0 )
  499.            *( pEnviron + count ) = ' ';
  500.        count++;
  501.    }
  502. }
  503.  
  504. //============================================================================
  505. //
  506. // Example # 10.
  507. //
  508. // This example is called with a variable number of parameters using
  509. // the func(...) syntax.
  510. //
  511. //============================================================================
  512.  
  513. DoubleType _cdecl _export AddNumbers( int pCount, DBaseVar *pFirstNum){
  514.  
  515.        // pFirstNum points to the parameters.
  516.    DBaseVar **ppVars = &pFirstNum;
  517.    DoubleType d = 0;
  518.  
  519.        // Go thru the parameters and add them.
  520.    while(pCount--){
  521.       d += (*ppVars)->Double();
  522.       ppVars++;
  523.    }
  524.    return d;
  525. }
  526.  
  527. //============================================================================
  528. //
  529. // Example 11.
  530. //
  531. // This example shows how a Visual dBASE object can be combined with an "hidden"
  532. // C++ sister object.  Each Visual dBASE object has its own unique C++ object.
  533. // This allows a Visual dBASE object to "remember and store" its own details
  534. // specific to the DLL in the C++ object instead of having to save it
  535. // in the Visual dBASE object. Examples are the storing of filehandles, and
  536. // filepointers when the DLL is servicing files.
  537. // Another possibility is a form of late binding where at the time of
  538. // creation of the C++ part a choice can be made from several derived
  539. // classes based on a common parent object.
  540. //
  541. //============================================================================
  542.  
  543. //
  544. // Common parent object
  545. //
  546. class DLLObject {
  547. protected:
  548.    char Message[60];
  549.    char ID[30];
  550. public:
  551.    DLLObject() {}
  552.    virtual char * Doit() = 0;
  553. };
  554.  
  555. //
  556. // ObjectA
  557. //
  558. class ObjectA : public DLLObject {
  559. public:
  560.    ObjectA( char *In ) { strcpy( ID, In ); }
  561.    char * Doit();
  562. };
  563.  
  564. char * ObjectA::Doit() {
  565.    strcpy( Message, ID );
  566.    strcat( Message, "you can read this" );
  567.    return Message;
  568. }
  569.  
  570. //
  571. // ObjectB
  572. //
  573. class ObjectB : public DLLObject {
  574. public:
  575.    ObjectB( char * In ) { strcpy( ID, In ); }
  576.    char * Doit();
  577. };
  578.  
  579. char * ObjectB::Doit() {
  580.    strcpy( Message, ID );
  581.    strcat( Message, "siht daer t'nac uoy" );
  582.    return Message;
  583. }
  584.  
  585. //
  586. // Helper function to retrieve the C++ this pointer from
  587. // the calling Visual dBASE object.
  588. //
  589. DLLObject *GetCPlusPlusThis() {
  590.  
  591.        // Local vars. Get the this of the Visual dBASE object.
  592.    DVar CThis, DBaseThis( DBase()->GetThis() );
  593.  
  594.        // Get the this of the corresponding C++ object.
  595.    DBaseThis->Property( "cthis", CThis );
  596.  
  597.    return (DLLObject*)(CThis->Long());
  598. }
  599.  
  600. //
  601. // Initializes the C++ object. Inserts the C++ this pointer into
  602. // the calling Visual dBASE object.
  603. //
  604. int _export _pascal DLLObjectInit( int Number ) {
  605.  
  606.    DLLObject * ThisDLLObject;
  607.    char Message[50];
  608.    static int Count = 0;
  609.  
  610.        // Get the this of the calling Visual dBASE object.
  611.    DVar CThis( DBase()->GetThis() );
  612.  
  613.        // Assign unique id, store object type.
  614.    sprintf( Message, "ID# %d, Type: Object #%d ", ++Count, Number );
  615.  
  616.        // Choose what kind of C++ object to create.
  617.    if( Number == 1 ) {
  618.  
  619. #ifdef NO_EXCEPTIONS
  620.  
  621.            // Create new C++ object.
  622.        ThisDLLObject = new ObjectA( Message );
  623.        if( ThisDLLObject == NULL )
  624.            return 0;
  625.  
  626.            // Sneak this pointer into the Visual dBASE object.
  627.        CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  628.    }
  629.    else {
  630.            // Create new C++ object.
  631.        ThisDLLObject = new ObjectB( Message );
  632.        if( ThisDLLObject == NULL )
  633.            return 0;
  634.  
  635.            // Sneak this pointer into the Visual dBASE object.
  636.        CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  637.  
  638. #else  // NO_EXCEPTIONS
  639.  
  640.        try {
  641.                // Create new C++ object.
  642.            ThisDLLObject = new ObjectA( Message );
  643.                // Sneak this pointer into the Visual dBASE object.
  644.            CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  645.        }
  646.        catch( ... ) {
  647.            return 0;
  648.        }
  649.    }
  650.    else {
  651.        try {
  652.                // Create new C++ object.
  653.            ThisDLLObject = new ObjectB( Message );
  654.                // Sneak this pointer into the Visual dBASE object.
  655.            CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  656.        }
  657.        catch( ... ) {
  658.            return 0;
  659.        }
  660.  
  661. #endif  // NO_EXCEPTIONS
  662.  
  663.    }
  664.  
  665.    return 1;
  666. }
  667.  
  668. //
  669. // Helper function to call the Doit function tied to the
  670. // calling Visual dBASE object.
  671. //
  672. char * _export _pascal Doit() {
  673.    return GetCPlusPlusThis()->Doit();
  674. }
  675.  
  676. //
  677. // Helper function to delete the C++ object tied to the calling
  678. // Visual dBASE object.
  679. //
  680. void _export _pascal Release() {
  681.    delete GetCPlusPlusThis();
  682. }
  683.  
  684.    // extern "C"
  685. }
  686.